package org.obp.data.support;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.obp.data.param.PageParam;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 查询条件解析器
 */
public class QueryResolver {

    /**
     * 构建单表查询条件
     *
     * @param query
     * @param <T>
     * @param <Q>
     * @return
     */
    public static <T, Q extends PageParam> QueryWrapper<T> buildQuery(Q query) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        for (Field field : getAllFields(query.getClass())) {
            field.setAccessible(true);
            // 查询字段标签
            QueryField annotation = field.getAnnotation(QueryField.class);
            if (annotation == null) {
                continue;
            }
            // 查询字段名称
            String fieldName = annotation.fieldName();
            if (StringUtils.isEmpty(fieldName)) {
                fieldName = camelToUnderline(field.getName());
            }
            // 查询字段的值（单个）
            Object filedValue;
            try {
                filedValue = field.get(query);
            } catch (IllegalAccessException e) {
                break;
            }
            // 查询字段的值（数组）
            Object[] fieldValues = new Object[]{};
            if (filedValue != null && filedValue.getClass().isArray()) {
                fieldValues = (Object[]) filedValue;
            }
            if (filedValue != null && filedValue.getClass().isAssignableFrom(ArrayList.class)) {
                ArrayList<Object> arrayList = (ArrayList<Object>) filedValue;
                fieldValues = arrayList.toArray();
            }

            // 根据查询条件构造predicates
            switch (annotation.operator()) {
                case EQ:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.eq(fieldName, filedValue);
                    }
                    break;
                case NE:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.ne(fieldName, filedValue);
                    }
                    break;
                case GE:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.ge(fieldName, filedValue);
                    }
                    break;
                case GT:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.gt(fieldName, filedValue);
                    }
                    break;
                case LE:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.le(fieldName, filedValue);
                    }
                    break;
                case LT:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.lt(fieldName, filedValue);
                    }
                    break;
                case LIKE:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.like(fieldName, filedValue);
                    }
                    break;
                case NOT_LIKE:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        queryWrapper.notLike(fieldName, filedValue);
                    }
                    break;
                case IN:
                    if (fieldValues.length > 0) {
                        queryWrapper.in(fieldName, fieldValues);
                    }
                    break;
                case NOT_IN:
                    if (fieldValues.length > 0) {
                        queryWrapper.notIn(fieldName, fieldValues);
                    }
                    break;
                case IS_NULL:
                    if (ObjectUtils.isNotEmpty(filedValue) && (int) filedValue == 1) {
                        queryWrapper.isNull(fieldName);
                    }
                    break;
                case IS_NOT_NULL:
                    if (ObjectUtils.isNotEmpty(filedValue) && (int) filedValue == 1) {
                        queryWrapper.isNotNull(fieldName);
                    }
                    break;
                case JSON_EQ:
                    if (ObjectUtils.isNotEmpty(filedValue)) {
                        String value = "'\"" + filedValue + "\"'";
                        String sql = "JSON_CONTAINS(#{fieldName}, #{filedValue})";
                        sql = sql.replace("#{fieldName}", fieldName);
                        sql = sql.replace("#{filedValue}", value);
                        queryWrapper.lambda().apply(sql);
                    }
                    break;
                case JSON_IN:
                    if (fieldValues.length > 0) {
                        String finalFieldName = fieldName;
                        Object[] finalFieldValues = fieldValues;
                        queryWrapper.and(wrapper -> {
                            for (Object valueItem : finalFieldValues) {
                                String value = "'\"" + valueItem + "\"'";
                                String sql = "JSON_CONTAINS(#{fieldName}, #{filedValue})";
                                sql = sql.replace("#{fieldName}", finalFieldName);
                                sql = sql.replace("#{filedValue}", value);
                                wrapper.lambda().apply(sql);
                            }
                        });
                    }
                    break;
                default:
                    break;
            }
        }
        return queryWrapper;
    }

    /**
     * 获取基类和父类所有的字段
     *
     * @param clazz
     * @return
     */
    private static Field[] getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    /**
     * 字段名称驼峰转下划线
     *
     * @param param
     * @return
     */
    private static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append('_');
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

}
